﻿using System;
using System.IO;
using ITSP.SharePoint.CacheRefresh.Common;
using Microsoft.Practices.SharePoint.Common.Logging;
using Microsoft.Practices.SharePoint.Common.ServiceLocation;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Administration;
using Microsoft.SharePoint.Utilities;

namespace ITSP.SharePoint.CacheRefresh.TimerJobs
{
    /// <summary>
    /// This timer job updates a file to cause a cache file to be invalidated.
    /// </summary>
    [Serializable()]
    public class InvalidateCacheFileJobDefinition : Microsoft.SharePoint.Administration.SPServiceJobDefinition
    {
        #region Private Members

        private const string CONST_ITSP_CACHELOCATION = "ITSP_TIMERJOB_PROPERTY_CACHEFILELOCATION";
        private ILogger _logger = Microsoft.Practices.SharePoint.Common.ServiceLocation.SharePointServiceLocator.GetCurrent().GetInstance<ILogger>();
        private string _jobName = "Cache Refresh Timer Job";
        private string _jobDescription = "Timer job which updates a file, this can be used to invalidate a cache using file dependencies.";
        
        /// <summary>
        /// Stores the Cache Dependency File Path.
        /// </summary>
        public string CacheDependencyFilePath
        {
            get
            {
                string cacheDependencyFilePath = null;
                if (this.Properties.ContainsKey(CONST_ITSP_CACHELOCATION) && Properties[CONST_ITSP_CACHELOCATION] != null)
                {
                    cacheDependencyFilePath = Properties[CONST_ITSP_CACHELOCATION].ToString();
                    
                    
                }
                return cacheDependencyFilePath;
            }

            set
            {
                this.Properties.Add(CONST_ITSP_CACHELOCATION, value);
            }
            
        }

        #endregion

        /// <summary>
        /// The Display Name of the Timer job.
        /// </summary>
        public override string DisplayName
        {
            get
            {
                return this.Name;
            }
        }

        /// <summary>
        /// The description of the timer job.
        /// </summary>
        public override string Description
        {
            get
            {
                return _jobDescription;
            }
        }

        #region Constructors

        /// <summary>
        /// Default Constructor
        /// </summary>
        public InvalidateCacheFileJobDefinition()
            : base()
        {
            
        }

        /// <summary>
        /// Constructor which creates the Invalidate Cache File Job
        /// </summary>
        /// <param name="name">the timer job name</param>
        /// <param name="service">the service to associate the timer job to.</param>
        /// <param name="cacheFilePath">the path to the file that the timer job will be modifying</param>
        public InvalidateCacheFileJobDefinition(string name, SPService service, string cacheFilePath)
            : base(name, service)
        {
            this.CacheDependencyFilePath = cacheFilePath;
        }

        #endregion

        /// <summary>
        /// Executes the timer job
        /// </summary>
        /// <param name="jobState">The Job State for Timer Job</param>
        public override void Execute(SPJobState jobState)
        {
            _logger.TraceToDeveloper(String.Format("InvalidateCacheFileJobDefinition::Execute(State: {0}) Enter", jobState.ToString()), 0, TraceSeverity.Verbose, Constants.Logging.DefaultLoggingCategory);
            using(SPMonitoredScope scope = new SPMonitoredScope("InvalidateCacheFileJobDefinition::Execute"))
            {
                try
                {
                    //create timer jobs based on the configuration
                    int percentageDone = 0;
                    this.UpdateProgress(percentageDone);

                    if(!System.IO.File.Exists(CacheDependencyFilePath))
                    {
                        _logger.TraceToDeveloper(String.Format("File {0} does not exist attempting to create", CacheDependencyFilePath), 0, TraceSeverity.Verbose, Constants.Logging.DefaultLoggingCategory);
                        using(StreamWriter stream = System.IO.File.CreateText(CacheDependencyFilePath))
                        {
                            stream.Close();
                        }
                        _logger.TraceToDeveloper(String.Format("File {0} Created as did not exist.", CacheDependencyFilePath), 0, TraceSeverity.Monitorable, Constants.Logging.DefaultLoggingCategory);
                    }

                    using(StreamWriter stream = System.IO.File.CreateText(CacheDependencyFilePath))
                    {
                        string updateText = String.Format("Last Updated {0} {1}", DateTime.Now.ToLongDateString(), DateTime.Now.ToLongTimeString());
                        stream.WriteLine(updateText);
                        stream.Close();
                        _logger.TraceToDeveloper(String.Format("Updated {0} with {1}", CacheDependencyFilePath, updateText), 0, TraceSeverity.Monitorable, Constants.Logging.DefaultLoggingCategory);
                    }

                    percentageDone = 100;
                    this.UpdateProgress(percentageDone);

                    DateTime nextRunTime = this.Schedule.NextOccurrence(DateTime.Now); //TODO: Seems to be giving back DateTime.Now;
                    _logger.TraceToDeveloper(String.Format("Completed {0} Next Run {1} @ {2}", this.DisplayName, nextRunTime.ToLongDateString(), nextRunTime.ToLongTimeString()), 0, TraceSeverity.Monitorable, Constants.Logging.DefaultLoggingCategory);
                }
                catch(System.IO.FileNotFoundException fileEx)
                {
                    _logger.LogToOperations(fileEx, 10000, EventSeverity.Error, Constants.Logging.DefaultLoggingCategory);
                    throw fileEx;
                }
                catch (Exception ex)
                {
                    _logger.TraceToDeveloper(ex, 10000, TraceSeverity.Unexpected, Constants.Logging.DefaultLoggingCategory);
                    _logger.LogToOperations(ex, 10000, EventSeverity.Error, Constants.Logging.DefaultLoggingCategory);
                    throw ex;
                }
            }

            _logger.TraceToDeveloper(String.Format("InvalidateCacheFileJobDefinition::Execute() Exit"), 0, TraceSeverity.Verbose, Constants.Logging.DefaultLoggingCategory);
        }

        /// <summary>
        /// Creates the Invalidate Cache File Job Definition
        /// </summary>
        /// <param name="name">Timer Job Name</param>
        /// <param name="service">SharePoint Service to run Timer Job to</param>
        /// <param name="numberOfMinutes">Number of Minutes to wait between each timer job</param>
        /// <param name="cacheFilePath">The file path to modify</param>
        /// <returns>The Timer Job Definition</returns>
        public static InvalidateCacheFileJobDefinition Create(string name, SPService service, int numberOfMinutes, string cacheFilePath)
        {
            InvalidateCacheFileJobDefinition job = null;

            try
            {
                SPSchedule schedule = null;
                if(numberOfMinutes>=60)
                {
                    SPHourlySchedule hourlySchedule=new SPHourlySchedule();
                    hourlySchedule.BeginMinute = 0;
                    hourlySchedule.EndMinute = 5;
                    schedule = hourlySchedule;
                }
                else
                {
                    SPMinuteSchedule minuteSchedule =new SPMinuteSchedule();   
                    minuteSchedule.Interval = numberOfMinutes;
                    schedule = minuteSchedule;
                }

                job = new InvalidateCacheFileJobDefinition(name, service, cacheFilePath);

                job.Schedule = schedule;
                job.Update(true);
            }
            catch (Exception ex)
            {

                throw;
            }

            return job;
        }

        /// <summary>
        /// Creates a Invalidate Cache File Job Definition using the Cache Configuraiton Item configuration settings.
        /// </summary>
        /// <param name="item">The Item that is the template to create the timer job definition</param>
        /// <param name="service">The SharePoint Service</param>
        /// <returns>The Timer Job Definition</returns>
        public static InvalidateCacheFileJobDefinition Create(SPListItem item, SPService service)
        {
            InvalidateCacheFileJobDefinition job = null;
            ILogger _logger = SharePointServiceLocator.GetCurrent().GetInstance<ILogger>();
            try
            {
                bool bCacheEnabled = item[Constants.Fields.CacheEnabled].ToString().ToLower() == "true";

                if(bCacheEnabled)
                {
                    string timerJobName = String.Format("Cache Refresh Job ({0})", item[Constants.Fields.Title].ToString());
                    string timerJobDescription = String.Format("{0}", item[Constants.Fields.CacheDescription].ToString());
                    string cachePath = String.Format("{0}", item[Constants.Fields.CacheFileDependencyPath].ToString());
                    
                    int cacheFrequency = 0;
                    Int32.TryParse(item[Constants.Fields.CacheFrequency].ToString(), out cacheFrequency);
                    if (cacheFrequency > 0)
                    {
                        job = Create(timerJobName, service, cacheFrequency, cachePath);
                    }
                }
            }
            catch (Exception ex)
            {
                _logger.TraceToDeveloper(ex, 10000, TraceSeverity.Unexpected, Constants.Logging.DefaultLoggingCategory);
                throw;
            }

            return job;
        }

        /// <summary>
        /// Creates a One Time Scheduled Invalidate Cache File Job Definition.
        /// Used to force a refresh process
        /// </summary>
        /// <param name="name">The Timer Job</param>
        /// <param name="service">The SharePoint Service to associate the timer job to.</param>
        /// <param name="cacheFilePath">The file path to modify</param>
        /// <returns>The Timer Job Defintion</returns>
        public static InvalidateCacheFileJobDefinition CreateOneTime(string name, SPService service, string cacheFilePath)
        {
            InvalidateCacheFileJobDefinition job = null;

            try
            {
                SPOneTimeSchedule schedule=new SPOneTimeSchedule(DateTime.Now);
                job = new InvalidateCacheFileJobDefinition(name, service, cacheFilePath);

                job.Schedule = schedule;
                job.Update(true);
            }
            catch (Exception ex)
            {

                throw;
            }

            return job;
        }


        /// <summary>
        /// Creates One Time Scheduled Cache File Job Definition on the Specified Server
        /// </summary>
        /// <param name="item">The Item that is the template to create the timer job definition</param>
        /// <param name="service">The SharePoint Service</param>
        /// <returns>The Timer Job Definition</returns>
        public static InvalidateCacheFileJobDefinition CreateOneTime(SPListItem item, SPService service)
        {
            InvalidateCacheFileJobDefinition job = null;
            ILogger _logger = SharePointServiceLocator.GetCurrent().GetInstance<ILogger>();
            try
            {
                bool bCacheEnabled = item[Constants.Fields.CacheEnabled].ToString().ToLower() == "true";

                if (bCacheEnabled)
                {
                    DateTime dateTime = DateTime.Now;
                    string timerJobName = String.Format("Cache Refresh Job ({0} - {1} @ {2})", item[Constants.Fields.Title].ToString(), dateTime.ToLongDateString(), dateTime.ToLongTimeString());
                    string timerJobDescription = String.Format("{0}", item[Constants.Fields.CacheDescription].ToString());
                    string cachePath = String.Format("{0}", item[Constants.Fields.CacheFileDependencyPath].ToString());

                    job = CreateOneTime(timerJobName, service, cachePath);
                }
            }
            catch (Exception ex)
            {
                _logger.TraceToDeveloper(ex, 10000, TraceSeverity.Unexpected, Constants.Logging.DefaultLoggingCategory);
                throw;
            }

            return job;
        }

        /// <summary>
        /// Deletes the Invalidate Cache File Job Definition from the farm.
        /// </summary>
        /// <returns>True/False depending on the operations success.</returns>
        public static bool DeleteAllJobs()
        {
            bool bSuccess = false;
            ILogger _logger = SharePointServiceLocator.GetCurrent().GetInstance<ILogger>();
            try
            {
                foreach(SPJobDefinition jobDefinition in SPFarm.Local.TimerService.JobDefinitions)
                {
                    InvalidateCacheFileJobDefinition checkDefinition = jobDefinition as InvalidateCacheFileJobDefinition;
                    if(checkDefinition!=null)
                    {
                        _logger.TraceToDeveloper(String.Format("Deleting Timer Job {0} on Service {1}", checkDefinition.DisplayName, checkDefinition.Service.DisplayName));
                        checkDefinition.Delete();
                    }
                }
                bSuccess = true;
            }
            catch (Exception ex)
            {
                _logger.TraceToDeveloper(ex, 10000, TraceSeverity.Unexpected, Constants.Logging.DefaultLoggingCategory);
                throw;
            }

            return bSuccess;
        }
    }
}
